package cn.sylinx.horm.proxy;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import cn.sylinx.horm.exception.HORMException;
import cn.sylinx.horm.util.ClassUtil;
import cn.sylinx.horm.util.GLog;

public enum ProxyObjectRegistry {

    INSTANCE;

    private final Map<Class<?>, ProxyFactory<?>> proxyFactoryMaps = new HashMap<Class<?>, ProxyFactory<?>>();

    public <T> boolean has(Class<T> type) {
        return proxyFactoryMaps.containsKey(type);
    }

    public <T> void register(Class<T> type) {
        if (type.isInterface()) {
            if (has(type)) {
                throw new HORMException("Type " + type + " is already known to the ProxyFactoryRegistry.");
            }
            proxyFactoryMaps.put(type, new ProxyFactory<T>(type));
            GLog.debug("h-orm proxy: {} registry ok", type.getName());
        }
    }

    @SuppressWarnings("unchecked")
    public <T> void register(String className) {
        Class<T> type = null;
        ClassLoader clToUse = ClassUtil.getDefaultClassLoader();
        try {
            type = (Class<T>) Class.forName(className, false, clToUse);
        } catch (Exception e) {
            GLog.error("no class defined", e);
        }
        if (type == null) {
            return;
        }
        register(type);
    }

    public <T> T getCommand(Class<T> type) {
        return get(type, ProxyType.command);
    }

    public <T> T getMapper(Class<T> type) {
        return get(type, ProxyType.mapper);
    }

    @SuppressWarnings("unchecked")
    private <T> T get(Class<T> type, ProxyType proxyType) {
        final ProxyFactory<T> proxyFactory = (ProxyFactory<T>) proxyFactoryMaps.get(type);
        if (proxyFactory == null) {
            throw new HORMException("Type " + type + " is not known to the ProxyFactoryRegistry.");
        }
        try {
            return proxyFactory.newInstance(new ProxyObject<T>(type, proxyType));
        } catch (Exception e) {
            throw new HORMException("Error getting proxy instance. Cause: " + e, e);
        }
    }

    public Collection<Class<?>> getAllProxyClass() {
        return Collections.unmodifiableCollection(proxyFactoryMaps.keySet());
    }
}